#ifndef SM2_ECC_H
#define SM2_ECC_H

#include "sm2_fp.h"
#include "asn1.h"

// Definition of structures ********************************************************************************************
/**
 * @struct sm2_point
 * @brief Defines the structure representing a point using byte array for SM2.
 * @struct sm2_apoint
 * @brief Defines the structure representing an affine point on the elliptic curve.
 * @struct sm2_jpoint
 * @brief Defines the structure representing a Jacobian point on the elliptic curve.
 */

typedef struct SM2Point{
    sm2_bn x;   ///< x-coordinate of the point
    sm2_bn y;   ///< y-coordinate of the point
} sm2_point;

typedef struct SM2AffinePoint{
    sm2_fp x;   ///< x-coordinate
    sm2_fp y;   ///< y-coordinate
} sm2_apoint;

typedef struct SM2JacobianPoint{
    sm2_fp x;   ///< x-coordinate
    sm2_fp y;   ///< y-coordinate
    sm2_fp z;   ///< z-coordinate
} sm2_jpoint;

// Declarations of functions *******************************************************************************************
/**
 * @brief Converts a string representation of a point to an SM2Point.
 * @param p Pointer to the destination SM2Point.
 * @param s String representation of the point.
 */
void sm2_point_from_str(sm2_point* p, const char *s);

/**
 * @brief Converts a byte array to an SM2Point.
 * @param p Pointer to the destination SM2Point.
 * @param b Byte array representation of the point.
 * @param blen Length of the byte array.
 */
void sm2_point_from_bytes(sm2_point *p, const u8 *b, const u32 blen);

/**
 * @brief Generates a random point on the elliptic curve using a scalar.
 * @param r Pointer to the resulting SM2Point.
 * @param k Scalar used for the generation.
 */
void sm2_point_gen(sm2_point *r, const sm2_fp k);

/**
 * @brief Multiplies a scalar with a point on the elliptic curve.
 * @param r Pointer to the resulting SM2Point.
 * @param k Scalar for multiplication.
 * @param p Pointer to the SM2Point to be multiplied.
 */
void sm2_point_mul(sm2_point *r, const sm2_fp k, const sm2_point *p);

/**
 * @brief Checks if a point is on the elliptic curve.
 * @param r Pointer to the SM2Point to be checked.
 * @return Returns 1 if the point is on the curve, 0 otherwise.
 */
int sm2_point_is_on_curve(const sm2_point *r);

/**
 * @brief Converts a point from SM2Point to SM2AffinePoint.
 * @param src Pointer to the source SM2Point.
 * @param p Pointer to the destination SM2AffinePoint.
 */
void sm2_point_to_apoint(const sm2_point *src, sm2_apoint *p);

/**
 * @brief Converts a point from SM2AffinePoint to SM2Point.
 * @param p Pointer to the source SM2AffinePoint.
 * @param dst Pointer to the destination SM2Point.
 */
void sm2_apoint_to_point(const sm2_apoint* p, sm2_point *dst);

/**
 * @brief Converts a string representation to an SM2AffinePoint.
 * @param p Pointer to the destination SM2AffinePoint.
 * @param s String representation of the affine point.
 */
void sm2_apoint_from_str(sm2_apoint *p, const char *s);

/**
 * @brief Converts a byte array to an SM2AffinePoint.
 * @param p Pointer to the destination SM2AffinePoint.
 * @param b Byte array representation of the affine point.
 * @param blen Length of the byte array.
 */
void sm2_apoint_from_bytes(sm2_apoint *p, const u8 *b, const u32 blen);

/**
 * @brief Sets the x and y coordinates of an SM2AffinePoint.
 * @param p Pointer to the destination SM2AffinePoint.
 * @param x X coordinate to set.
 * @param y Y coordinate to set.
 */
void sm2_apoint_set(sm2_apoint *p, const sm2_fp x, const sm2_fp y);

/**
 * @brief Checks if an affine point represents the point at infinity.
 * @param r Pointer to the SM2AffinePoint to be checked.
 * @return Returns 1 if the point is at infinity, 0 otherwise.
 */
int sm2_apoint_at_infinity(const sm2_apoint *r);

/**
 * @brief Multiplies a scalar with an affine point.
 * @param r Pointer to the resulting SM2AffinePoint.
 * @param k Scalar for multiplication.
 * @param p Pointer to the SM2AffinePoint to be multiplied.
 */
void sm2_apoint_mul(sm2_apoint *r, const sm2_fp k, const sm2_apoint *p);

/**
 * @brief Generates a random affine point using a scalar.
 * @param r Pointer to the resulting SM2AffinePoint.
 * @param k Scalar used for the generation.
 */
void sm2_apoint_gen(sm2_apoint *r, const sm2_fp k);

/**
 * @brief Multiplies two affine points together.
 * @param r Pointer to the resulting SM2AffinePoint.
 * @param s First scalar.
 * @param t Second scalar.
 * @param q Pointer to the SM2AffinePoint to be multiplied.
 */
void sm2_apoint_mutmul(sm2_apoint *r, sm2_fp s, sm2_fp t, const sm2_apoint *q);

/**
 * @brief Converts an affine point to a Jacobian point.
 * @param p Pointer to the source SM2AffinePoint.
 * @param r Pointer to the destination SM2JacobianPoint.
 */
void sm2_apoint_to_jpoint(const sm2_apoint *p, sm2_jpoint *r);

/**
 * @brief Converts a Jacobian point to an affine point.
 * @param a Pointer to the source SM2JacobianPoint.
 * @param r Pointer to the destination SM2AffinePoint.
 */
void sm2_jpoint_to_apoint(const sm2_jpoint *a, sm2_apoint *r);

/**
 * @brief Copies the content of one Jacobian point to another.
 * @param p Pointer to the destination SM2JacobianPoint.
 * @param q Pointer to the source SM2JacobianPoint.
 */
void sm2_jpoint_copy(sm2_jpoint *p, const sm2_jpoint *q);

/**
 * @brief Sets the coordinates of a Jacobian point.
 * @param p Pointer to the destination SM2JacobianPoint.
 * @param x X coordinate to set.
 * @param y Y coordinate to set.
 * @param z Z coordinate to set.
 */
void sm2_jpoint_set(sm2_jpoint *p, const sm2_fp x, const sm2_fp y, const sm2_fp z);

/**
 * @brief Sets a Jacobian point to the point at infinity.
 * @param r Pointer to the SM2JacobianPoint to be set.
 */
void sm2_jpoint_set_infinity(sm2_jpoint *r);

/**
 * @brief Checks if a jpoint is at infinity.
 * @param r The jpoint to check.
 * @return 1 if the jpoint is at infinity, 0 otherwise.
 */
int sm2_jpoint_at_infinity(const sm2_jpoint *r);

/**
 * @brief Converts a string representation to a Jacobian point.
 * @param p Pointer to the destination SM2JacobianPoint.
 * @param s String representation of the Jacobian point.
 */
void sm2_jpoint_from_str(sm2_jpoint *p, const char *s);

/**
 * @brief Adds two Jacobian points.
 * @param r Pointer to the resulting SM2JacobianPoint.
 * @param p Pointer to the first SM2JacobianPoint.
 * @param q Pointer to the second SM2JacobianPoint.
 */
void sm2_jpoint_add(sm2_jpoint *r, const sm2_jpoint *p, const sm2_jpoint *q);

/**
 * @brief Subtracts one Jacobian point from another.
 * @param r Pointer to the resulting SM2JacobianPoint.
 * @param p Pointer to the minuend SM2JacobianPoint.
 * @param q Pointer to the subtrahend SM2JacobianPoint.
 */
void sm2_jpoint_sub(sm2_jpoint *r, const sm2_jpoint *p, const sm2_jpoint *q);

/**
 * @brief Point addition, affine-Jacobian coordinates
 * @param r The result of the addition.
 * @param p The first jpoint.
 * @param q The second point.
 */
void sm2_japoint_add(sm2_jpoint *r, const sm2_jpoint *p, const sm2_apoint *q);

/**
 * @brief Point subtraction, affine-Jacobian coordinates
 * @param r The result of the subtraction.
 * @param p The first jpoint.
 * @param q The second point.
 */
void sm2_japoint_sub(sm2_jpoint *r, const sm2_jpoint *p, const sm2_apoint *q);

/**
 * @brief Doubles a Jacobian point.
 * @param r Pointer to the resulting SM2JacobianPoint.
 * @param a Pointer to the SM2JacobianPoint to be doubled.
 */
void sm2_jpoint_dou(sm2_jpoint *r, const sm2_jpoint *a);

/**
 * @brief Performs scalar multiplication on a Jacobian point.
 * @param r Pointer to the resulting SM2JacobianPoint.
 * @param m Scalar for multiplication.
 * @param p Pointer to the SM2JacobianPoint to be multiplied.
 */
void sm2_jpoint_mdo(sm2_jpoint *r, u32 m, const sm2_jpoint *p);

/**
 * @brief Multiplies a scalar with a Jacobian point.
 * @param r Pointer to the resulting SM2JacobianPoint.
 * @param k Scalar for multiplication.
 * @param g Pointer to the SM2JacobianPoint to be multiplied.
 */
void sm2_jpoint_mul(sm2_jpoint *r, const sm2_fp k, const sm2_jpoint *g);

/**
 * @brief Generates a random Jacobian point using a scalar.
 * @param r Pointer to the resulting SM2JacobianPoint.
 * @param k Scalar used for the generation.
 */
void sm2_jpoint_gen(sm2_jpoint *r, const sm2_fp k);

#define Large_lookup_table_allowed 0

#if Large_lookup_table_allowed == 1
    extern const sm2_apoint sm2_pct[32][256];
#else
    extern const sm2_apoint fpt[52];
#endif


#endif // SM2_ECC_H